package common

import (
	"fmt"
	"gitee.com/go-web-2/orm_framework/errors"
	"gitee.com/go-web-2/orm_framework/predicate"
	"gitee.com/go-web-2/orm_framework/session/db"
	"gitee.com/go-web-2/orm_framework/sql"
	"strings"
)

type SentenceBuilder struct {
	Builder strings.Builder
	db.Core
	Args []interface{}
}

func (b *SentenceBuilder) GenerateField(fieldName string) string {
	return fmt.Sprintf("%s%s%s", b.Dialect.Quote(), fieldName, b.Dialect.Quote())
}

// BuildJoin 用在from处，可定义别名，不可使用别名
func (b *SentenceBuilder) BuildJoin(entity predicate.Join) error {
	b.Builder.WriteString("(")
	err := b.buildTableReference(entity.Left, sql.UseAlias{Build: true, Use: false})
	if err != nil {
		return err
	}

	b.Builder.WriteString(" " + entity.Op.String() + " ")
	err = b.buildTableReference(entity.Right, sql.UseAlias{Build: true, Use: false})
	if err != nil {
		return err
	}

	if len(entity.On) > 0 {
		b.Builder.WriteString(" On ")
		b.Builder.WriteString("(")
		for i, on := range entity.On {
			// table1.column = table2.Column2
			err = b.BuildPredicate(on, sql.UseAlias{Build: false, Use: true})
			if err != nil {
				return err
			}
			if i != len(entity.On)-1 {
				b.Builder.WriteString(" " + predicate.OpAnd.String() + " ")
			}
		}
		b.Builder.WriteString(")")
	} else if len(entity.Using) > 0 {
		b.Builder.WriteString(" Using ")
		b.Builder.WriteString("(")
		for i, use := range entity.Using {
			err = b.BuildColumn(use, sql.UseAlias{Build: false, Use: true})
			if err != nil {
				return err
			}
			if i != len(entity.Using)-1 {
				b.Builder.WriteString(",")
			}
		}
		b.Builder.WriteString(")")
	} else {
		return errors.ErrJoinStat
	}

	b.Builder.WriteString(")")

	if entity.Alias != "" {
		b.BuildAs(entity.Alias)
	}

	return nil
}

func (b *SentenceBuilder) BuildSubQuery(subQuery predicate.SubQuery, alias sql.UseAlias) error {
	if alias.Use && subQuery.Alias != "" {
		b.Builder.WriteString(b.GenerateField(subQuery.Alias))
		return nil
	}
	queryS, err := subQuery.QueryBuilder.Build()
	query := queryS[0] // 兼容分库分表
	if err != nil {
		return err
	}
	// query.SQL[:len(query.SQL)-1 目的：移除语句中的分号
	b.Builder.WriteString("(" + query.SQL[:len(query.SQL)-1] + ")")
	b.Args = append(b.Args, query.Args...)
	if alias.Build && subQuery.Alias != "" {
		b.BuildAs(subQuery.Alias)
	}
	return nil
}

func (b *SentenceBuilder) buildTableReference(tableReference predicate.TableReference, alias sql.UseAlias) error {
	switch tr := tableReference.(type) {
	case predicate.Table:
		b.BuildTable(tr, alias)
	case predicate.Join:
		err := b.BuildJoin(tr)
		if err != nil {
			return err
		}
	case predicate.SubQuery:
		err := b.BuildSubQuery(tr, alias)
		if err != nil {
			return err
		}
	default:
		return errors.ErrUnknownTable(tableReference)
	}
	return nil
}

// BuildTable 用在from、join on处; from可定义别名，不可使用别名; join on 处不可定义别名，可使用别名
func (b *SentenceBuilder) BuildTable(table predicate.Table, alias sql.UseAlias) {
	if alias.Use && table.Alias != "" {
		b.Builder.WriteString(b.GenerateField(table.Alias))
	} else {
		m := b.R.Get(table.Entity) // join查询使用
		b.Builder.WriteString(b.GenerateField(m.TableName))
	}

	if alias.Build && table.Alias != "" {
		b.BuildAs(table.Alias)
	}
}

func (b *SentenceBuilder) BuildColumn(col predicate.Column, alias sql.UseAlias) error {
	m := b.Model
	if col.Entity != nil {
		// col.Entity不为nil时是使用的join查询，存在多个model，需要重新依据entity获取对应model
		table, ok := col.Entity.(predicate.Table)
		if !ok {
			return errors.ErrUnknownTable(col.Entity)
		}
		m = b.R.Get(table.Entity) // join查询使用
		err := b.buildTableReference(col.Entity, alias)
		if err != nil {
			return err
		}
		// 示例：table1.Column1
		b.Builder.WriteString(".")
	}
	field, ok := m.ColToFieldMap[col.ColumnName]
	if !ok {
		return errors.ErrNotFoundColumn(col.ColumnName)
	}

	if alias.Use && col.Alias != "" {
		// 使用别名
		b.Builder.WriteString(b.GenerateField(col.Alias))
	} else {
		b.Builder.WriteString(b.GenerateField(field.FieldName))
	}

	if alias.Build && col.Alias != "" {
		b.BuildAs(col.Alias)
	}

	return nil
}

// BuildPredicate 别名：Predicate一般用在where部分、having、join on部分，where处不能定义、使用别名，having处的列可以使用别名，不能定义别名；join on处的列可以使用别名
// 所以Predicate本身不可以定义、使用别名；但在having、join on处，Predicate中的列可以使用别名
func (b *SentenceBuilder) BuildPredicate(p predicate.Predicate, alias sql.UseAlias) error {
	// Not id < 5 时， p.Left为nil
	if p.Left != nil {
		if left, ok := p.Left.(predicate.Predicate); ok {
			b.Builder.WriteString("(")
			if err := b.BuildPredicate(left, alias); err != nil {
				return err
			}
			b.Builder.WriteString(")")
		} else {
			if err := b.BuildExpression(p.Left, alias); err != nil {
				return err
			}
		}

		// Raw.AsPredicate() 没有op和right
		if p.Op != "" {
			b.Builder.WriteString(" ")
		}

	}

	// Raw.AsPredicate() 没有op和right
	if p.Op != "" {
		b.Builder.WriteString(p.Op.String())
		b.Builder.WriteString(" ")
	}

	if p.Right != nil {
		if right, ok := p.Right.(predicate.Predicate); ok {
			if p.Left != nil {
				b.Builder.WriteString("(")
			}
			if err := b.BuildPredicate(right, alias); err != nil {
				return err
			}
			if p.Left != nil {
				b.Builder.WriteString(")")
			}
		} else {
			if err := b.BuildExpression(p.Right, alias); err != nil {
				return err
			}
		}
	}

	return nil
}

func (b *SentenceBuilder) BuildAggregate(aggregate predicate.Aggregate, alias sql.UseAlias) error {
	if alias.Use && aggregate.Alias != "" {
		b.Builder.WriteString(b.GenerateField(aggregate.Alias))
	} else {
		b.Builder.WriteString(aggregate.Op.String())
		b.Builder.WriteString("(")
		err := b.BuildColumn(aggregate.Column, sql.UseAlias{Build: false, Use: false})
		if err != nil {
			return err
		}
		b.Builder.WriteString(")")
	}

	if alias.Build && aggregate.Alias != "" {
		b.BuildAs(aggregate.Alias)
	}
	return nil
}

func (b *SentenceBuilder) BuildAs(str string) {
	b.Builder.WriteString(" As ")
	b.Builder.WriteString(b.GenerateField(str))
}

func (b *SentenceBuilder) BuildRawExpr(raw predicate.RawExpr) error {
	b.Builder.WriteString("(" + raw.Sql + ")")
	b.Args = append(b.Args, raw.Args...)
	return nil
}

func (b *SentenceBuilder) BuildExpression(expr predicate.Expression, alias sql.UseAlias) error {
	switch exp := expr.(type) {
	case predicate.Column:
		return b.BuildColumn(exp, alias)
	case predicate.Predicate:
		return b.BuildPredicate(exp, alias)
	case predicate.Valuer:
		b.Builder.WriteString("?")
		b.Args = append(b.Args, exp.Val)
	case predicate.Aggregate:
		return b.BuildAggregate(exp, alias)
	case predicate.RawExpr:
		return b.BuildRawExpr(exp)
	case predicate.SubQuery:
		return b.BuildSubQuery(exp, alias)
	default:
		return errors.ErrInvalidExpression(exp)
	}
	return nil
}
